<template>
  <div
    :class="['s-dialog',customClass,position?'s-dialog-position-'+position:'',{'s-dialog-visible':effectStatus}]"
    :style="'z-index:'+zIndex+';'+styleDisplay+styleDuration"
  >
    <template v-if="mask">
      <div
        class="s-dialog-mask"
				@click="maskClose && hide()"
        :style="'background-color: rgba(0, 0, 0, '+maskOpacity+');'"
      ></div>
    </template>
    <div class="s-dialog-wrapper">
      <slot></slot>
    </div>
  </div>
</template>

<script>
const nextFrame = (() => {
  const raf = setTimeout
  return fn => raf(() => raf(fn, 5), 5)
})()

export default {
  name: 's-dialog',
  props: {
    customClass: {
      type: String,
      default: ''
    },
    value: Boolean,
    position: {
      type: String,
      default: 'middle'
    },
    zIndex: {
      type: Number,
      default: 2000
    },
    mask: {
      type: Boolean,
      default: true
    },
    maskOpacity: {
      type: Number,
      default: 0.7
    },
    maskClose: {
      type: Boolean,
      default: true
    },
    showDuration: {
      type: Number,
      default: 400
    },
    hideDuration: {
      type: Number,
      default: 400
    },
    showBefore: Function, // 返回promise拦截
    showAfter: Function,
    hideBefore: Function, // 返回promise拦截
    hideAfter: Function
  },
  data () {
    return {
      visibleId: 0,
      timeOut: 0,
      effectStatus: false,
      dialogVisible: false,
      styleDisplay: '',
      styleDuration: ''
    }
  },
  watch: {
    value () {
      this.updateVisible()
    }
  },
  methods: {
    async show () {
      if (!this.dialogVisible) {
        this.visibleId++
        let status = true
        const nowId = this.visibleId
        if (this.showBefore) {
          try {
            await this.showBefore()
          } catch (error) {
            status = false
          }
        }
        if (nowId === this.visibleId) {
          if (status) {
            this.dialogVisible = true
            this.$emit('input', true)
            this.styleDuration = 'transition-duration:' + this.showDuration + 'ms;'
            this.styleDisplay = 'display:block;'
            clearTimeout(this.timeOut)
            this.$nextTick(() => {
              nextFrame(() => {
                this.effectStatus = true
                this.timeOut = setTimeout(() => {
                  this.styleDuration = ''
                  if (this.dialogVisible) {
                    this.showAfter && this.showAfter()
                  }
                }, this.showDuration)
              })
            })
          } else {
            this.$emit('input', false)
          }
        }
      }
    },
    async hide () {
      if (this.dialogVisible) {
        this.visibleId++
        let status = true
        const nowId = this.visibleId
        if (this.hideBefore) {
          try {
            await this.hideBefore()
          } catch (error) {
            status = false
          }
        }
        if (nowId === this.visibleId) {
          if (status) {
            this.dialogVisible = false
            this.$emit('input', false)
            this.styleDuration = 'transition-duration:' + this.hideDuration + 'ms;'
            clearTimeout(this.timeOut)
            this.$nextTick(() => {
              nextFrame(() => {
                this.effectStatus = false
                this.timeOut = setTimeout(() => {
                  this.styleDisplay = ''
                  this.styleDuration = ''
                  if (!this.dialogVisible) {
                    this.hideAfter && this.hideAfter()
                  }
                }, this.hideDuration)
              })
            })
          } else {
            this.$emit('input', true)
          }
        }
      }
    },
    updateVisible () {
      this.$nextTick(() => {
        if (this.dialogVisible !== this.value) {
          this[this.value ? 'show' : 'hide']()
        }
      })
    }
  },
  mounted () {
    this.updateVisible()
  }
}
</script>

<style lang="scss">
.s-dialog {
  position: fixed;
  display: none;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  margin: 0;
  padding: 0;
  font-size: 0;
  letter-spacing: 0;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  &,
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
  &:after {
    content: "";
    display: inline-block;
    vertical-align: middle;
    line-height: 0;
    width: 0;
    height: 100%;
  }
  .s-dialog-mask {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
  }
  .s-dialog-wrapper {
    position: relative;
    display: block;
    text-align: left;
    white-space: normal;
    letter-spacing: normal;
    font-size: 28upx;
    max-width: 100%;
    max-height: 100%;
    z-index: 2;
    overflow: auto;
    overscroll-behavior: contain;
    backface-visibility: hidden;
    transform-style: preserve-3d;
  }
  //弹框效果
  .s-dialog-mask,
  .s-dialog-wrapper {
    transition-duration: inherit;
    transition-timing-function: ease;
    transform-style: preserve-3d;
    backface-visibility: hidden;
    opacity: 0;
  }

  &.s-dialog-visible {
    .s-dialog-mask,
    .s-dialog-wrapper {
      opacity: 1;
    }
  }
  //middle
  &.s-dialog-position-middle {
    .s-dialog-wrapper {
      display: inline-block;
      vertical-align: middle;
      transform: scale(0.8, 0.8);
    }
    &.s-dialog-visible {
      .s-dialog-wrapper {
        transform: scale(1, 1);
      }
    }
  }
  //top
  &.s-dialog-position-top {
    .s-dialog-wrapper {
      transform: translateY(-100%);
    }
    &.s-dialog-visible {
      .s-dialog-wrapper {
        transform: translateY(0);
      }
    }
  }
  //bottom
  &.s-dialog-position-bottom {
    .s-dialog-wrapper {
      transform: translateY(100%);
    }
    &.s-dialog-visible {
      .s-dialog-wrapper {
        transform: translateY(0);
      }
    }
  }
  //从左侧滑入
  &.s-dialog-position-left {
    .s-dialog-wrapper {
      transform: translateX(-100%);
    }
    &.s-dialog-visible {
      .s-dialog-wrapper {
        transform: translateX(0);
      }
    }
  } //从右侧滑入
  &.s-dialog-position-right {
    .s-dialog-wrapper {
      transform: translateX(100%);
    }
    &.s-dialog-visible {
      .s-dialog-wrapper {
        transform: translateX(0);
      }
    }
  }
}
</style>
